package dd

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"log"
	"net/http"
	"net/url"
	"sync"
	"time"
)

type ApiResponse struct {
	St   int    `json:"st"`
	Msg  string `json:"msg"`
	Code any    `json:"code"`
	Data Data   `json:"data"`
}

type Data struct {
	PendingTotalAmount string      `json:"pending_total_amount"`
	SkuOrderList       []SkuOrder  `json:"sku_order_list"`
	PopupCode          string      `json:"popup_code"`
	Explanation        Explanation `json:"explanation"`
	Page               int         `json:"page"`
	Size               int         `json:"size"`
	Total              int         `json:"total"`
}

type SkuOrder struct {
	SkuOrderID          string       `json:"sku_order_id"`
	ShopOrderID         string       `json:"shop_order_id"`
	ProductID           string       `json:"product_id"`
	RefundInfo          any          `json:"refund_info"`
	CreateTime          string       `json:"create_time"`
	ReceiptTime         string       `json:"receipt_time"`
	PayAmount           string       `json:"pay_amount"`
	PendingSettleAmount string       `json:"pending_settle_amount"`
	OrderStatusDesc     string       `json:"order_status_desc"`
	PendingSettleTime   []SettleTime `json:"pending_settle_time"`
	PlaceHolder         any          `json:"place_holder"`
}

type SettleTime struct {
	TimeText  string `json:"time_text"`
	HoverText string `json:"hover_text"`
	ColorType int    `json:"color_type"`
}

type Explanation struct {
	TextDetail []TextDetail `json:"text_detail"`
	URLMap     URLMap       `json:"url_map"`
}

type TextDetail struct {
	Key   string `json:"key"`
	Label string `json:"label"`
	Value string `json:"value"`
	Hover string `json:"hover"`
}

type URLMap struct {
	FastCollection Link `json:"fast_collection"`
}

type Link struct {
	Text           string      `json:"text"`
	URL            string      `json:"url"`
	Type           string      `json:"type"`
	ComponentProps interface{} `json:"component_props"`
	RichHover      interface{} `json:"rich_hover"`
}

type Result struct {
	Page        int
	ApiResponse *ApiResponse
	Error       error
}

func setHeaders(req *http.Request) {
	// req.Header.Set("__x_pid", "/ffa/fxg-bill/order-bill")
	// req.Header.Set("accept", "application/json, text/plain, */*")
	// req.Header.Set("accept-language", "zh-CN,zh;q=0.9")
	// req.Header.Set("cache-control", "no-cache")
	req.Header.Set("content-type", "application/json")
	// req.Header.Set("origin", "https://fxg.jinritemai.com")
	// req.Header.Set("pragma", "no-cache")
	// req.Header.Set("referer", "https://fxg.jinritemai.com/ffa/fxg-bill/order-bill")
	// req.Header.Set("user-agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36")
	// req.Header.Set("x-secsdk-csrf-token", `000100000001f9a15fbbf3c4b38be74f1259b137d1af22ec230857da9a359e64bfe4f80357df1802886dd2b14b55`)
	req.Header.Set("cookie", `PHPSESSID=3de34982b8ea60b36c94052373108aa9;`)
}

func fetchPage(page int, results chan<- Result) {
	// page 从0开始
	baseURL := "https://fxg.jinritemai.com/api/order/pendingSettle/list"

	query := url.Values{}
	// query.Set("msToken", "afwB157ZE7RbPCwn8dlmdDJKydtV34bv5DiJPlVMFigDBhKiiBheAP-gq5rimKD4nqaioKnOkUrEHbIbVMy4gbZZQJzM_m7umMiOFMEB8RYCOkDpr91ZGCTibCIbtaobeG-UqlO44jcLMD02ZM7wmwm3YRldz0I%3D")
	// query.Set("a_bogus", "Of8MBdgXdEVTffEk5X5LfY3qI6-RYDzZ0Gi0MDZMxDp0Eg39HMPS9exEvItvVZYjNT%2FdICujy4hbO3xprQCr81wf7Wsx%2F2CZs640tMBd5xSi5Z4nuymMrsJo5Jb3SFlm5XNAEOhmy75nFmT0AoKa-kIWTDeSYrv%2FHjkrPVnbZVyHNKX71AB-toW%3D")
	// query.Set("verifyFp", "verify_m22x6hiz_9yMLBXfE_4jGy_4yEB_92BE_HnGKa6WIK3aD")
	// query.Set("fp", "verify_m22x6hiz_9yMLBXfE_4jGy_4yEB_92BE_HnGKa6WIK3aD")

	urlWithQuery := fmt.Sprintf("%s?%s", baseURL, query.Encode())

	pageSize := 100
	payload := fmt.Sprintf(`{"order":"desc","order_by":"create_time","page":%d,"pageSize":%d,"time_type":"pending_settle_time","order_status":"","aid":4272,"appid":1}`, page, pageSize)
	req, err := http.NewRequest("POST", urlWithQuery, bytes.NewBuffer([]byte(payload)))
	if err != nil {
		results <- Result{Page: page, Error: err}
		return
	}

	setHeaders(req)

	client := &http.Client{Timeout: 30 * time.Second}

	response, err := client.Do(req)
	if err != nil {
		// 等待500毫秒钟重试一次
		time.Sleep(500 * time.Millisecond)
		response, err = client.Do(req)
		if err != nil {
			results <- Result{Page: page, Error: err}
			return
		}
	}
	defer response.Body.Close()

	body, err := io.ReadAll(response.Body)
	if err != nil {
		results <- Result{Page: page, Error: err}
		return
	}

	var apiResponse ApiResponse
	err = json.Unmarshal(body, &apiResponse)
	if err != nil {
		results <- Result{Page: page, Error: err}
		return
	}

	results <- Result{Page: page, ApiResponse: &apiResponse}
}

func worker(pages <-chan int, results chan<- Result, wg *sync.WaitGroup) {
	defer wg.Done()
	for page := range pages {
		fetchPage(page, results)
	}
}

func fillPages(pages chan<- int, numPages int) {
	// api页码从0开始
	for i := 0; i < numPages; i++ {
		pages <- i
	}
	close(pages)
}

func processResults(results <-chan Result, wg *sync.WaitGroup) {
	defer wg.Done()
	for result := range results {
		if result.Error != nil {
			log.Printf("Error fetching page %d: %v", result.Page, result.Error)
		} else {
			switch code := result.ApiResponse.Code.(type) {
			case string:
				// 失败
				log.Printf("Page %d processed with error code: %s", result.Page, code)
			case float64:
				// 成功
				if code == 0 && result.ApiResponse != nil {
					// 打印和网页页码对应
					fmt.Printf("Page %d PageSize %d PendingTotalAmount %s processed successfully.\n", result.Page+1, len(result.ApiResponse.Data.SkuOrderList), result.ApiResponse.Data.PendingTotalAmount)
				}
			}
		}
	}
}

func waitAndCloseResults(wg *sync.WaitGroup, results chan Result) {
	wg.Wait()
	close(results)
}

func processAllResults(results <-chan Result, numWorkers int) {
	var processWg sync.WaitGroup
	processWg.Add(numWorkers)
	for i := 0; i < numWorkers; i++ {
		go processResults(results, &processWg)
	}

	// 等待所有结果处理完成
	processWg.Wait()
}

func startWorkers(pages chan int, results chan Result, numPages, numWorkers int) {
	var wg sync.WaitGroup

	// 启动协程填充页码
	go fillPages(pages, numPages)

	wg.Add(numWorkers)
	for i := 0; i < numWorkers; i++ {
		// 启动工作协程获取数据
		go worker(pages, results, &wg)
	}

	// 等待所有worker完成并关闭结果通道
	go waitAndCloseResults(&wg, results)
}

func calculateWorkersAndPages(numPages int) (int, int) {
	// 计算workers数量和页码数量关系
	var numWorkers int
	if numPages <= 200 {
		numWorkers = numPages
	} else {
		numWorkers = 200
	}

	return numPages, numWorkers
}

func Crawler() {
	numPages, numWorkers := calculateWorkersAndPages(1)
	pages := make(chan int, numPages)
	results := make(chan Result, numPages)
	startTime := time.Now()

	// 启动worker
	startWorkers(pages, results, numPages, numWorkers)
	// 并发处理结果
	processAllResults(results, numWorkers)

	endTime := time.Since(startTime)
	fmt.Printf("Execution time: %.2f seconds\n", endTime.Seconds())
}
